#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

/*

	pid_t getpid(void)	// 获取当前进程 pid
	pid_t getppid(void)	// 获取当前进程父进程的 pid
	
	// 文档查询命令
	man getpid
	
	// 涉及头文件
	#include <sys/types.h>
    #include <unistd.h>

*/
int main() {
 
 	// pid_t 是一个数据类型，实际上是一个整型
 	// 通过 typedef 重新定义了一个名字，用于存储进程id
	pid_t cpid;
	
	printf("before fork() perform, pid=%d\n", getpid());
	
	int p = 100;
	printf("before fork(), p=%d, %p\n", p, &p);
	
	/*
	fork() 函数用于创建一个新的进程，新创建的进程为该进程的子进程
	
	fork()函数的返回值:
		创建成功:对于父子进程，fork()函数会返回不同值
			对于父进程: 返回值是新创建的子进程 pid 
			对于子进程: 返回值是 0
		创建失败: 返回值为 -1
	*/ 
	cpid = fork();
	
	printf("after fork() perform: pid=%d\n", getpid());
	
	// 注意: 父子进程代码是并发执行的，可以通过调整 for 循环的次数来验证 3->3000
	
	if (cpid == 0) {
		// 子进程返回时执行
		printf("------------------------\n");
		
		printf("child process, pid=%d, ppid=%d, cpid=%d\n", getpid(), getppid(), cpid); 
		for (int i = 0; i < 3; i++) {
			printf("hello\n");
		}
		
		// 子进程和父进程的内存空间不一样，此时父子进程操作变量 p 不会相互干扰
		// 这里返回的 p 变量值不一样，但是 &p 取到的地址是一样的，原因是因为这里的 &p 是虚拟内存
		p++;
		printf("child process, p=%d, &p=%p\n", p, &p);
		
	
 	} else {
		// 父进程返回时执行
		printf("------------------------\n");
		
		printf("parent process, pid=%d, cpid=%d\n", getpid(), cpid);
		for (int i = 0; i < 3; i++) {
			printf("world\n");
		}
		
		p--;
		printf("parent process, p=%d, &p=%p\n", p, &p);
		
		// 注意: 如果控制台输出的日志错乱（此时，子进程的 ppid=1），则需要加上 wait() 函数
		// 表示父进程需要等子进程结束后再结束
		// 问题原因: fork() 函数的两次返回时，由于父子进程的代码是并发执行的，如果父进程先执行结束
		// 那么在 return 0 之后，父进程就结束了，此时会将子进程托管给系统进程 (pid=1)
		wait(NULL);
	}

	return 0;
}



